#include "MultinomialSorter.h"
#include <math.h>

static const double PI = 3.14159265358979;
static const double E = 2.718281828459045;

// n! ~= sqrt(2*Pi*n) * (n / E)^n = (2*Pi)^0.5 * n^(n + 0.5) / E^n
static inline double logFactorial(uint n) {
	if (0 == n) {
		return 0;
	} else {
		return 0.5 * log(2 * PI) + (n + 0.5) * log((double)n) - n * log(E);
	}
}



MultinomialSorter::MultinomialSorter(const Dictionary & dictionary)
	: BaseSorter(dictionary)
{
}

int MultinomialSorter::getCategoryIndexOf(const Document & document) {
	std::vector<double> sum(dictionary.getCategoriesCount());

	for (uint i = 0; i < dictionary.getCategoriesCount(); ++i) {
		sum[i] = logFactorial(document.getWordsCountMap().size());

		const WordsCountMap & wcm = document.getWordsCountMap();
		for (WordsCountMap::const_iterator iter=wcm.begin(); iter != wcm.end(); ++iter) {
			TermsMap::const_iterator dicIter = dictionary.getTermsMap().find(iter->first);
			if ((dictionary.getTermsMap().end() == dicIter) || (!dicIter->second.concerned)) {
				continue;
			}
			const Term & term = dicIter->second;

			sum[i] += iter->second * logPwc(term, i) - logFactorial(iter->second);
		}
	}

	double max = -1.7e+300;
	int maxindex = 0;
	for (uint i = 0; i < sum.size(); ++i) {
		if (sum[i] > max) {
			max = sum[i];
			maxindex = i;
		}
	}
	return maxindex;
}

double MultinomialSorter::logPwc(const Term & term, uint categoryIndex) {
	return log((double)1 + term.totalInCategory[categoryIndex]) -
		log((double)dictionary.getTotalTerms() + dictionary.getCategory(categoryIndex).totalTermsCount);
}
